package org.study.util;

import com.google.common.collect.Range;

import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.Map;

/**
 * 类的描述
 *
 * @author Administrator
 * @date 2021-06-03
 */
public final class BigDecimalUtils {
    public static void main(String[] args) {
        String format = getPercentage(new BigDecimal(40), new BigDecimal(7), 5, 2, BigDecimal.ROUND_HALF_UP);
        System.out.println(format);
    }

    // 地球半径，平均半径为6371km
    private static final Integer RADIUS = 6378137;
    public static final BigDecimal ZERO = new BigDecimal("0");
    public static final BigDecimal ONE = new BigDecimal("1");
    public static final BigDecimal TEN = new BigDecimal("10");
    public static final BigDecimal FIFTY = new BigDecimal("50");
    public static final BigDecimal SIXTY = new BigDecimal("60");
    public static final BigDecimal HUNDRED = new BigDecimal("100");
    public static final BigDecimal THOUSAND = new BigDecimal("1000");

    private BigDecimalUtils() {
        throw new AssertionError("不能实例化工具类！");
    }

    public static BigDecimal float2BigDecimal(float f) {
        String tmp = Float.toString(f);
        BigDecimal value = new BigDecimal(tmp);
        return value;
    }

    public static BigDecimal double2BigDecimal(double d) {
        String tmp = Double.toString(d);
        BigDecimal value = new BigDecimal(tmp);
        return value;
    }

    /**
     * 设置精度：四舍五入，保留2位小数
     *
     * @param num 原始值
     * @return java.math.BigDecimal
     */
    public static final BigDecimal setPrecision(BigDecimal num) {
        Assert.notNull(num, "设置精度运算的原始值不能为null");
        if (num == null) {
            return null;
        }
        BigDecimal ret = num.setScale(2, BigDecimal.ROUND_HALF_UP);
        return ret;
    }

    /**
     * 设置精度：四舍五入，保留指定小数位
     *
     * @param num      原始值
     * @param newScale 几位小数
     * @return java.math.BigDecimal
     */
    public static final BigDecimal setPrecision(BigDecimal num, int newScale) {
        Assert.notNull(num, "设置精度运算的原始值不能为null");
        if (num == null) {
            return null;
        }
        BigDecimal ret = num.setScale(newScale, BigDecimal.ROUND_HALF_UP);
        return ret;
    }

    /**
     * 设置精度：指定取舍模式，保留指定小数位
     *
     * @param num          原始值
     * @param newScale     几位小数
     * @param roundingMode 取舍模式
     * @return java.math.BigDecimal
     */
    public static final BigDecimal setPrecision(BigDecimal num, int newScale, int roundingMode) {
        Assert.notNull(num, "设置精度运算的原始值不能为null");
        if (num == null) {
            return null;
        }
        BigDecimal ret = num.setScale(newScale, roundingMode);
        return ret;
    }

    /**
     * 比较大小
     *
     * @param num1
     * @param num2
     * @return int
     */
    public static final int compareTo(BigDecimal num1, BigDecimal num2) {
        Assert.notNull(num1, "比较大小运算的参数1不能为null");
        Assert.notNull(num2, "比较大小运算的参数2不能为null");
        int result = num1.compareTo(num2);
        return result;
    }

    /**
     * 判断给定值是否为 0
     *
     * @param num
     * @return
     */
    public static boolean isZero(BigDecimal num) {
        BigDecimal zero = BigDecimal.ZERO;
        int i = zero.compareTo(num);
        if (i == 0) {
            return true;
        }
        return false;
    }

    /**
     * 判断 num1、num2 是否相等
     *
     * @param num1
     * @param num2
     * @return
     */
    public static boolean isEquals(BigDecimal num1, BigDecimal num2) {
        int i = num1.compareTo(num2);
        if (i == 0) {
            return true;
        }
        return false;
    }

    /**
     * 判断 num1 是否小于 num2
     *
     * @param num1
     * @param num2
     * @return
     */
    public static boolean isLessThan(BigDecimal num1, BigDecimal num2) {
        int i = num1.compareTo(num2);
        if (i < 0) {
            return true;
        }
        return false;
    }

    /**
     * 判断 num1 是否小于等于 num2
     *
     * @param num1
     * @param num2
     * @return
     */
    public static boolean isLessThanEquals(BigDecimal num1, BigDecimal num2) {
        int i = num1.compareTo(num2);
        if (i <= 0) {
            return true;
        }
        return false;
    }

    /**
     * 判断 num1 是否大于 num2
     *
     * @param num1
     * @param num2
     * @return
     */
    public static boolean isGreaterThan(BigDecimal num1, BigDecimal num2) {
        int i = num1.compareTo(num2);
        if (i > 0) {
            return true;
        }
        return false;
    }

    /**
     * 判断 num1 是否大于等于 num2
     *
     * @param num1
     * @param num2
     * @return
     */
    public static boolean isGreaterThanEquals(BigDecimal num1, BigDecimal num2) {
        int i = num1.compareTo(num2);
        if (i >= 0) {
            return true;
        }
        return false;
    }

    /**
     * 加法：四舍五入，保留2位小数
     *
     * @param num1 被加数
     * @param num2 加数
     * @return java.math.BigDecimal
     */
    public static BigDecimal add(BigDecimal num1, BigDecimal num2) {
        Assert.notNull(num1, "加法运算的被加数不能为null");
        Assert.notNull(num2, "加法运算的加数不能为null");
        BigDecimal result = num1.add(num2).setScale(2, BigDecimal.ROUND_HALF_UP);
        return result;
    }

    /**
     * 加法：四舍五入，保留指定小数位
     *
     * @param num1     被加数
     * @param num2     加数
     * @param newScale 几位小数
     * @return java.math.BigDecimal
     */
    public static BigDecimal add(BigDecimal num1, BigDecimal num2, int newScale) {
        Assert.notNull(num1, "加法运算的被加数不能为null");
        Assert.notNull(num2, "加法运算的加数不能为null");
        BigDecimal result = num1.add(num2).setScale(newScale, BigDecimal.ROUND_HALF_UP);
        return result;
    }

    /**
     * 加法：指定取舍模式，保留指定小数位
     *
     * @param num1         被加数
     * @param num2         加数
     * @param newScale     几位小数
     * @param roundingMode 取舍模式
     * @return java.math.BigDecimal
     */
    public static BigDecimal add(BigDecimal num1, BigDecimal num2, int newScale, int roundingMode) {
        Assert.notNull(num1, "加法运算的被加数不能为null");
        Assert.notNull(num2, "加法运算的加数不能为null");
        BigDecimal result = num1.add(num2).setScale(newScale, roundingMode);
        return result;
    }

    /**
     * 減法：四舍五入，保留2位小数
     *
     * @param num1 被减数
     * @param num2 减数
     * @return java.math.BigDecimal
     */
    public static BigDecimal subtract(BigDecimal num1, BigDecimal num2) {
        Assert.notNull(num1, "減法运算的被減数不能为null");
        Assert.notNull(num2, "減法运算的減数不能为null");
        BigDecimal result = num1.subtract(num2).setScale(2, BigDecimal.ROUND_HALF_UP);
        return result;
    }

    /**
     * 減法：四舍五入，保留指定小数位
     *
     * @param num1     被减数
     * @param num2     减数
     * @param newScale 几位小数
     * @return java.math.BigDecimal
     */
    public static BigDecimal subtract(BigDecimal num1, BigDecimal num2, int newScale) {
        Assert.notNull(num1, "減法运算的被減数不能为null");
        Assert.notNull(num2, "減法运算的減数不能为null");
        BigDecimal result = num1.subtract(num2).setScale(newScale, BigDecimal.ROUND_HALF_UP);
        return result;
    }

    /**
     * 減法：指定取舍模式，保留指定小数位
     *
     * @param num1         被减数
     * @param num2         减数
     * @param newScale     几位小数
     * @param roundingMode 取舍模式
     * @return java.math.BigDecimal
     */
    public static BigDecimal subtract(BigDecimal num1, BigDecimal num2, int newScale, int roundingMode) {
        Assert.notNull(num1, "減法运算的被減数不能为null");
        Assert.notNull(num2, "減法运算的減数不能为null");
        BigDecimal result = num1.subtract(num2).setScale(newScale, roundingMode);
        return result;
    }

    /**
     * 乘法：四舍五入，保留2位小数
     *
     * @param num1 被乘数
     * @param num2 乘数
     * @return java.math.BigDecimal
     */
    public static BigDecimal multiply(BigDecimal num1, BigDecimal num2) {
        Assert.notNull(num1, "乘法运算的被乘数不能为null");
        Assert.notNull(num2, "乘法运算的乘数不能为null");
        BigDecimal result = num1.multiply(num2).setScale(2, BigDecimal.ROUND_HALF_UP);
        return result;
    }

    /**
     * 乘法：四舍五入，保留指定小数位
     *
     * @param num1     被乘数
     * @param num2     乘数
     * @param newScale 几位小数
     * @return java.math.BigDecimal
     */
    public static BigDecimal multiply(BigDecimal num1, BigDecimal num2, int newScale) {
        Assert.notNull(num1, "乘法运算的被乘数不能为null");
        Assert.notNull(num2, "乘法运算的乘数不能为null");
        BigDecimal result = num1.multiply(num2).setScale(newScale, BigDecimal.ROUND_HALF_UP);
        return result;
    }

    /**
     * 乘法：指定取舍模式，保留指定小数位
     *
     * @param num1         被乘数
     * @param num2         乘数
     * @param newScale     几位小数
     * @param roundingMode 取舍模式
     * @return java.math.BigDecimal
     */
    public static BigDecimal multiply(BigDecimal num1, BigDecimal num2, int newScale, int roundingMode) {
        Assert.notNull(num1, "乘法运算的被乘数不能为null");
        Assert.notNull(num2, "乘法运算的乘数不能为null");
        BigDecimal result = num1.multiply(num2).setScale(newScale, roundingMode);
        return result;
    }

    /**
     * 除法：四舍五入，保留2位小数
     *
     * @param num1 被除数
     * @param num2 除数
     * @return java.math.BigDecimal
     */
    public static BigDecimal divide(BigDecimal num1, BigDecimal num2) {
        Assert.notNull(num1, "除法运算的被除数不能为null");
        Assert.notNull(num2, "除法运算的除数不能为null");
        Assert.isTrue(BigDecimal.ZERO.compareTo(num2) != 0, "除法运算的除数不能为0");
        BigDecimal result = num1.divide(num2).setScale(2, BigDecimal.ROUND_HALF_UP);
        return result;
    }

    /**
     * 除法：四舍五入，保留指定小数位
     *
     * @param num1     被除数
     * @param num2     除数
     * @param newScale 几位小数
     * @return java.math.BigDecimal
     */
    public static BigDecimal divide(BigDecimal num1, BigDecimal num2, int newScale) {
        Assert.notNull(num1, "除法运算的被除数不能为null");
        Assert.notNull(num2, "除法运算的除数不能为null");
        Assert.isTrue(BigDecimal.ZERO.compareTo(num2) != 0, "除法运算的除数不能为0");
        BigDecimal result = num1.divide(num2).setScale(newScale, BigDecimal.ROUND_HALF_UP);
        return result;
    }

    /**
     * 除法：指定取舍模式，保留指定小数位
     *
     * @param num1         被除数
     * @param num2         除数
     * @param newScale     几位小数
     * @param roundingMode 取舍模式
     * @return java.math.BigDecimal
     */
    public static BigDecimal divide(BigDecimal num1, BigDecimal num2, int newScale, int roundingMode) {
        Assert.notNull(num1, "除法运算的被除数不能为null");
        Assert.notNull(num2, "除法运算的除数不能为null");
        Assert.isTrue(BigDecimal.ZERO.compareTo(num2) != 0, "除法运算的除数不能为0");
        BigDecimal result = num1.divide(num2).setScale(newScale, roundingMode);
        return result;
    }

    /**
     * 取余：返回 BigDecimal 数组，其中包含两个元素，第一个为两数相除的商，第二个为两数相除的余数
     *
     * @param num1 被除数
     * @param num2 除数
     * @return java.math.BigDecimal[]
     */
    public static BigDecimal[] divideAndRemainder(BigDecimal num1, BigDecimal num2) {
        Assert.notNull(num1, "除法运算(返回商和余数)的被除数不能为null");
        Assert.notNull(num2, "除法运算(返回商和余数)的除数不能为null");
        Assert.isTrue(BigDecimal.ZERO.compareTo(num2) != 0, "除法运算(返回商和余数)的除数不能为0");
        BigDecimal[] result = num1.divideAndRemainder(num2);
        return result;
    }

    /**
     * 两个 BigDecimal 相除取商
     *
     * @param num1 被除数
     * @param num2 除数
     * @return java.math.BigDecimal
     */
    public static BigDecimal getDivide(BigDecimal num1, BigDecimal num2) {
        Assert.notNull(num1, "除法运算(返回商)的被除数不能为null");
        Assert.notNull(num2, "除法运算(返回商)的除数不能为null");
        Assert.isTrue(BigDecimal.ZERO.compareTo(num2) != 0, "除法运算(返回商)的除数不能为0");
        BigDecimal[] result = num1.divideAndRemainder(num2);
        return result[0];
    }

    /**
     * 两个 BigDecimal 相除取余
     *
     * @param num1 被除数
     * @param num2 除数
     * @return java.math.BigDecimal
     */
    public static BigDecimal getRemainder(BigDecimal num1, BigDecimal num2) {
        Assert.notNull(num1, "除法运算(返回余数)的被除数不能为null");
        Assert.notNull(num2, "除法运算(返回余数)的除数不能为null");
        Assert.isTrue(BigDecimal.ZERO.compareTo(num2) != 0, "除法运算(返回余数)的除数不能为0");
        BigDecimal[] result = num1.divideAndRemainder(num2);
        return result[1];
    }

    /**
     * 两个 BigDecimal 相除成百分比，默认四舍五入，5位数字，2位小数
     *
     * @param num1 被除数
     * @param num2 除数
     * @return java.lang.String
     */
    public static String getPercentage(BigDecimal num1, BigDecimal num2) {
        BigDecimal r = num1.divide(num2, 5, BigDecimal.ROUND_HALF_UP);
        NumberFormat percent = NumberFormat.getPercentInstance();
        percent.setMaximumFractionDigits(2);
        String format = percent.format(r.doubleValue());
        return format;
    }

    /**
     * 两个 BigDecimal 相除成百分比，默认四舍五入，5位数字，2位小数
     *
     * @param num1         被除数
     * @param num2         除数
     * @param scale        数字位数
     * @param newValue     小数位数
     * @param roundingMode 取舍模式
     * @return java.lang.String
     */
    public static String getPercentage(BigDecimal num1, BigDecimal num2, int scale, int newValue, int roundingMode) {
        BigDecimal r = num1.divide(num2, scale, roundingMode);
        NumberFormat percent = NumberFormat.getPercentInstance();
        percent.setMaximumFractionDigits(newValue);
        String format = percent.format(r.doubleValue());
        return format;
    }

    /**
     * 取绝对值
     *
     * @param num 数
     * @return java.math.BigDecimal
     */
    public static BigDecimal abs(BigDecimal num) {
        Assert.notNull(num, "取绝对值运算的参数不能为null");
        BigDecimal result = num.abs();
        return result;
    }

    /**
     * 取相反数
     *
     * @param num 数
     * @return java.math.BigDecimal
     */
    public static BigDecimal negate(BigDecimal num) {
        Assert.notNull(num, "取相反数运算的参数不能为null");
        BigDecimal result = num.negate();
        return result;
    }

    /**
     * 小数点左移
     *
     * @param num 数
     * @param n   移动位数
     * @return java.math.BigDecimal
     */
    public static BigDecimal movePointLeft(BigDecimal num, int n) {
        Assert.notNull(num, "小数点左移运算的参数不能为null");
        BigDecimal result = num.movePointLeft(n);
        return result;
    }

    /**
     * 小数点右移
     *
     * @param num 数
     * @param n   移动位数
     * @return java.math.BigDecimal
     */
    public static BigDecimal movePointRight(BigDecimal num, int n) {
        Assert.notNull(num, "小数点右移运算的参数不能为null");
        BigDecimal result = num.movePointRight(n);
        return result;
    }

    /**
     * 判断 num 是否在 (lower, upper) 之间
     *
     * @param num
     * @param lower
     * @param upper
     * @return boolean
     */
    public static boolean isBetweenAllOpen(BigDecimal num, BigDecimal lower, BigDecimal upper) {
        Range<BigDecimal> closed = Range.open(lower, upper);
        boolean contains = closed.contains(num);
        return contains;
    }

    /**
     * 判断 num 是否在 (lower, upper] 之间
     *
     * @param num
     * @param lower
     * @param upper
     * @return boolean
     */
    public static boolean isBetweenOpenClosed(BigDecimal num, BigDecimal lower, BigDecimal upper) {
        Range<BigDecimal> closed = Range.openClosed(lower, upper);
        boolean contains = closed.contains(num);
        return contains;
    }

    /**
     * 判断 num 是否在 [lower, upper) 之间
     *
     * @param num
     * @param lower
     * @param upper
     * @return boolean
     */
    public static boolean isBetweenClosedOpen(BigDecimal num, BigDecimal lower, BigDecimal upper) {
        Range<BigDecimal> closed = Range.closedOpen(lower, upper);
        boolean contains = closed.contains(num);
        return contains;
    }

    /**
     * 判断 num 是否在 [lower, upper] 之间
     *
     * @param num
     * @param lower
     * @param upper
     * @return boolean
     */
    public static boolean isBetweenAllClosed(BigDecimal num, BigDecimal lower, BigDecimal upper) {
        Range<BigDecimal> closed = Range.closed(lower, upper);
        boolean contains = closed.contains(num);
        return contains;
    }

    /**
     * 取 num1 和 num2 中的大值
     *
     * @param num1
     * @param num2
     * @return
     */
    public static BigDecimal max(BigDecimal num1, BigDecimal num2) {
        int i = num1.compareTo(num2);
        if (i >= 0) {
            return num1;
        }
        return num2;
    }

    /**
     * 取 num1 和 num2 中的小值
     *
     * @param num1
     * @param num2
     * @return
     */
    public static BigDecimal min(BigDecimal num1, BigDecimal num2) {
        int i = num1.compareTo(num2);
        if (i <= 0) {
            return num1;
        }
        return num2;
    }

    /**
     * 根据经纬度算出附近的正方形的四个角的经纬度
     *
     * @param longitude 精度
     * @param latitude  纬度
     * @param distance  附近多少米
     * @return
     */
    public static Map<String, double[]> returnLLSquarePoint(double longitude, double latitude, double distance) {
        Map<String, double[]> squareMap = new HashMap();
        // 计算经度弧度，从弧度转换为角度
        double dLongitude = 2 * (Math.asin(Math.sin(distance / (2 * RADIUS)) / Math.cos(Math.toRadians(latitude))));
        dLongitude = Math.toDegrees(dLongitude);
        // 计算纬度角度
        double dLatitude = distance / RADIUS;
        dLatitude = Math.toDegrees(dLatitude);
        // 正方形
        double[] leftTopPoint = {longitude - dLongitude, latitude + dLatitude};
        double[] rightTopPoint = {longitude + dLongitude, latitude + dLatitude};
        double[] leftBottomPoint = {longitude - dLongitude, latitude - dLatitude};
        double[] rightBottomPoint = {longitude + dLongitude, latitude - dLatitude};
        squareMap.put("leftTopPoint", leftTopPoint);
        squareMap.put("rightTopPoint", rightTopPoint);
        squareMap.put("leftBottomPoint", leftBottomPoint);
        squareMap.put("rightBottomPoint", rightBottomPoint);
        return squareMap;
    }
}
